/* COPYRIGHT_CHUNFENG */
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include "cf_key_value_file.h"
#include "cf_std.h"
#include "cf_common.h"

CFKeyValuePair* cf_key_value_pair_new(int is_comment, const char* key, const char* value)
{
	CFKeyValuePair* pair = (CFKeyValuePair*)malloc(sizeof(CFKeyValuePair));
	pair->is_comment = is_comment;
	pair->key = cf_string_new(key);
	pair->value = cf_string_new(value);
	cf_list_init(&pair->list);
	return pair;
}

void cf_key_value_pair_free(CFListHead* node)
{
    CFKeyValuePair* pair = container_of(node, CFKeyValuePair, list);
	cf_string_free(pair->key);
	cf_string_free(pair->value);
	free(pair);
}


int cf_key_value_pair_write(CFListHead* listNode, int fd, char eq)
{
	int res = 0;
	int keyLen, valueLen;
    CFKeyValuePair* pair = container_of(listNode, CFKeyValuePair, list);
	if(!pair->is_comment){
		keyLen = pair->key->len;
		res += write(fd, cf_key_value_pair_get_key(pair), keyLen);
		res += write(fd, &eq, 1);	
		if(res != keyLen + 1) return -1; 
	}
	res = 0;
	valueLen = (pair->value->len);
	res += write(fd, cf_key_value_pair_get_value(pair), valueLen);
	if(res != valueLen) return -1; 
	return res;
}

inline int cf_key_eq_value_pair_write(CFListHead* listNode, int fd)
{
    return cf_key_value_pair_write(listNode, fd, '=');
}

inline int cf_key_blank_value_pair_write(CFListHead* listNode, int fd)
{
    return cf_key_value_pair_write(listNode, fd, ' ');
}

static CFListHead* _parse_key_value_pair_line_new(char* start, char* end, char eq)
{
	CFKeyValuePair* pair;
	CFString* tmpStr;
	char *key_start, *key_end, *value_start, *value_end, *equal;
	
	//printf("end_n exists start =%c, end=%d\n", *start , end);
	cf_char_strip(&start, &end, TRUE, TRUE);
	//printf("end_n exists start =%d, end=%d\n", start , end);
	/* line with only one character */
	if(start == end){
		if(cf_char_is_blank(*start)) 
			return &cf_key_value_pair_new(TRUE, "", "")->list;
		else if(*start != '#') 
			return NULL;
	}
	
	/* comment line */
	if(*start == '#'){
		CFString *tmpStr = cf_string_new_len(start, end-start+1);
		pair = cf_key_value_pair_new(TRUE, "", tmpStr->str);
		cf_string_free(tmpStr);
		return &pair->list;
	}
	
    if(eq == ' ' || eq == '\t'){
        char* b, *t;
        b = memchr(start, ' ', end-start+1);
        t = memchr(start, '\t', end-start+1);
        if(!b&&t){
            equal = t;
        }else if(b&&!t){
            equal = b;
        }else if(b&&t){
            equal = b<t?b:t;
        }else{
            equal = NULL;
        }
    }else{
        equal = memchr(start, eq, end-start+1);
    }
	if(NULL == equal) return NULL;
	key_start = start;
	key_end = equal - 1;
	cf_char_strip(&key_start, &key_end, FALSE, TRUE);
	tmpStr = cf_string_new_len(key_start, key_end - key_start + 1);
	pair = cf_key_value_pair_new(FALSE, "", "");
	cf_key_value_pair_set_key(pair, tmpStr->str);
	cf_string_free(tmpStr);
	
	value_start = equal + 1;
	value_end = end;
	cf_char_strip(&value_start, &value_end, TRUE, FALSE);
	tmpStr = cf_string_new_len(value_start, value_end - value_start +1);
	cf_key_value_pair_set_value(pair, tmpStr->str);
	cf_string_free(tmpStr);
	return &pair->list;
}

static CFListHead* _parse_key_eq_value_pair_line_new(char* start, char* end)
{
    return _parse_key_value_pair_line_new(start, end, '=');
}

static CFListHead* _parse_key_blank_value_pair_line_new(char* start, char* end)
{
    return _parse_key_value_pair_line_new(start, end, ' ');
}

CFKeyValueFile* cf_key_value_file_new(const char* fileUri, char eq)
{
    CFKeyValueFile* file;
    CFListHead* list;
    
    if(eq == '='){
        list = cf_list_file_new(fileUri, _parse_key_eq_value_pair_line_new);
    }else if(eq == ' ' || eq == '\t'){
        list = cf_list_file_new(fileUri, _parse_key_blank_value_pair_line_new);
    }else{
        return NULL;
    }
    if(!list)
        return NULL;
    file = (CFKeyValueFile*)malloc(sizeof(CFKeyValueFile));
    cf_list_init(&file->pairHead.list);
    cf_list_insert_pre(list, &file->pairHead.list);
    file->fileUri = cf_string_new(fileUri);
    file->eq = eq;
    return file;
}

void cf_key_value_file_free(CFKeyValueFile* file)
{
    cf_list_file_free(&file->pairHead.list, cf_key_value_pair_free);
	cf_string_free(file->fileUri);
	free(file);
}

int cf_key_value_file_write(CFKeyValueFile* file, int fd)
{
    if(file->eq == '='){
        return cf_list_file_write(&file->pairHead.list, fd, cf_key_eq_value_pair_write);
    }else if(file->eq == ' ' || file->eq == '\t'){
        return cf_list_file_write(&file->pairHead.list, fd, cf_key_blank_value_pair_write);
    }
    return -1;
}

int cf_key_value_file_print(CFKeyValueFile* file)
{
    return cf_key_value_file_write(file, 1);
}

int cf_key_value_file_save(CFKeyValueFile* file, const char* fileUri)
{
    if(!fileUri)
        fileUri = cf_string_get_str(file->fileUri);
    if(file->eq == '=')
        return cf_list_file_save(&file->pairHead.list, fileUri, cf_key_eq_value_pair_write);
    else if(file->eq == ' ' || file->eq == '\t')
        return cf_list_file_save(&file->pairHead.list, fileUri, cf_key_blank_value_pair_write);
    return -1;
}

const char* cf_key_value_file_get(CFKeyValueFile* file, const char* key)
{
	CFKeyValuePair *tmpPair = CFListGetNextContainer(&file->pairHead.list, CFKeyValuePair, list);;
	do{
		if(!cf_key_value_pair_is_comment(tmpPair) && 0 == strcmp(cf_key_value_pair_get_key(tmpPair), key))
			return cf_key_value_pair_get_value(tmpPair);
        tmpPair = CFListGetNextContainer(&tmpPair->list, CFKeyValuePair, list);
	}while(tmpPair != &file->pairHead);
	return NULL;
}

void cf_key_value_file_set(CFKeyValueFile* file, const char* key, const char* value)
{
	CFKeyValuePair *tmpPair = CFListGetNextContainer(&file->pairHead.list, CFKeyValuePair, list);

	do{
		if(!cf_key_value_pair_is_comment(tmpPair) 
                && 0 == strcmp(cf_key_value_pair_get_key(tmpPair), key)){
			cf_key_value_pair_set_value(tmpPair, value);
			return;
		}
        tmpPair = CFListGetNextContainer(&tmpPair->list, CFKeyValuePair, list);
	}while(tmpPair != &file->pairHead);
	cf_list_insert_pre(&file->pairHead.list, &cf_key_value_pair_new(FALSE, key, value)->list);
}

void cf_key_value_file_set_by_pair(CFKeyValueFile* file, CFKeyValuePair* pair)
{
    
}
